/**
 * 
 */
package com.dragonwarriors.player;

import com.dalonedrau.d20.Diceroller;
import com.dalonedrau.pool.PooledStringBuilder;
import com.dalonedrau.pool.StringBuilderPool;

/**
 * @author Donald
 */
public final class DWPlayer {
	/** the one and only instance of the <code>Player</code> class. */
	private static DWPlayer	instance;
	/**
	 * Player Gives access to the singleton instance of {@link DWPlayer}.
	 * @return {@link DWPlayer}
	 */
	public static DWPlayer getInstance() {
		if (DWPlayer.instance == null) {
			DWPlayer.instance = new DWPlayer();
		}
		return DWPlayer.instance;
	}
	/** Hidden constructor. */
	private DWPlayer() { }
	public int getAssignableSkillsForLevelUp(final DWCharacter pc) {
		int skills = 0;
		switch (pc.getProfession()) {
		case DWConsts.PROF_ASSASSIN:
			switch (pc.getRank()) {
			case 1: // character creation
				// can choose one combat skill
				skills |= DWConsts.SKILL_ASSASSIN_COMBAT_ARMOUR_PIERCING;
				skills |= DWConsts.SKILL_ASSASSIN_COMBAT_SHOCK_ATTACK;
				skills |= DWConsts.SKILL_ASSASSIN_COMBAT_THROWING_SPIKES;
				skills |= DWConsts.SKILL_ASSASSIN_COMBAT_UNARMED_COMBAT;
				// can choose one meditation
				skills |= DWConsts.SKILL_ASSASSIN_MEDITATION_LIGHT_TRANCE;
				// can choose one assassin skill
				skills |= DWConsts.SKILL_ASSASSIN_SKILL_ALCHEMY;
				skills |= DWConsts.SKILL_ASSASSIN_SKILL_BREAKFALL_1;
				skills |= DWConsts.SKILL_ASSASSIN_SKILL_CLIMBING_1;
				skills |= DWConsts.SKILL_ASSASSIN_SKILL_DISGUISE_1;
				skills |= DWConsts.SKILL_ASSASSIN_SKILL_PICK_LOCK_1;
				skills |= DWConsts.SKILL_ASSASSIN_SKILL_PILFER_1;
				break;
			case 2: // offer of darkness trance or another skill
				skills |= DWConsts.SKILL_ASSASSIN_MEDITATION_DARKNESS_TRANCE;
				// offer light trance if not taken at level 1
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_MEDITATION_LIGHT_TRANCE)) {
					skills |= DWConsts.SKILL_ASSASSIN_MEDITATION_LIGHT_TRANCE;
				}
				// offer an assassin skill not taken, but only level 1
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_ALCHEMY)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_ALCHEMY;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_BREAKFALL_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_BREAKFALL_1;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_CLIMBING_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_CLIMBING_1;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_DISGUISE_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_DISGUISE_1;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_PICK_LOCK_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_PICK_LOCK_1;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_PILFER_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_PILFER_1;
				}
				break;
			case 3:
				// offer a 2nd combat skill
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_COMBAT_ARMOUR_PIERCING)) {
					skills |= DWConsts.SKILL_ASSASSIN_COMBAT_ARMOUR_PIERCING;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_COMBAT_SHOCK_ATTACK)) {
					skills |= DWConsts.SKILL_ASSASSIN_COMBAT_SHOCK_ATTACK;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_COMBAT_THROWING_SPIKES)) {
					skills |= DWConsts.SKILL_ASSASSIN_COMBAT_THROWING_SPIKES;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_COMBAT_UNARMED_COMBAT)) {
					skills |= DWConsts.SKILL_ASSASSIN_COMBAT_UNARMED_COMBAT;
				}
				// offer an assassin skill not taken, but only up to level 2
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_ALCHEMY)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_ALCHEMY;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_BREAKFALL_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_BREAKFALL_1;
				} else {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_BREAKFALL_2;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_CLIMBING_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_CLIMBING_1;
				} else {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_CLIMBING_2;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_DISGUISE_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_DISGUISE_1;
				} else {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_DISGUISE_2;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_PICK_LOCK_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_PICK_LOCK_1;
				} else {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_PICK_LOCK_2;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_PILFER_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_PILFER_1;
				} else {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_PILFER_2;
				}
				break;
			case 4:
				// offer a mental skill
				skills |= DWConsts.SKILL_ASSASSIN_MENTAL_INNER_SENSE;
				skills |= DWConsts.SKILL_ASSASSIN_MENTAL_MEMORIZE;
				// offer meditation skills not taken yet
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_MEDITATION_LIGHT_TRANCE)) {
					skills |= DWConsts.SKILL_ASSASSIN_MEDITATION_LIGHT_TRANCE;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_MEDITATION_DARKNESS_TRANCE)) {
					skills |= DWConsts.SKILL_ASSASSIN_MEDITATION_DARKNESS_TRANCE;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_MEDITATION_WATER_TRANCE)) {
					skills |= DWConsts.SKILL_ASSASSIN_MEDITATION_WATER_TRANCE;
				}
				// offer an assassin skill not taken, but only up to level 2
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_ALCHEMY)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_ALCHEMY;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_BREAKFALL_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_BREAKFALL_1;
				} else {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_BREAKFALL_2;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_CLIMBING_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_CLIMBING_1;
				} else {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_CLIMBING_2;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_DISGUISE_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_DISGUISE_1;
				} else {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_DISGUISE_2;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_PICK_LOCK_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_PICK_LOCK_1;
				} else {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_PICK_LOCK_2;
				}
				if (!pc.hasSkill(DWConsts.SKILL_ASSASSIN_SKILL_PILFER_1)) {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_PILFER_1;
				} else {
					skills |= DWConsts.SKILL_ASSASSIN_SKILL_PILFER_2;
				}
				break;
			}
			break;
		case DWConsts.PROF_BARBARIAN:
			if (pc.getRank() >= 8
					&& !pc.hasSkill(DWConsts.SKILL_BARBARIAN_BLOODRAGE)) {
				skills |= DWConsts.SKILL_BARBARIAN_BLOODRAGE;
			}
			break;
		case DWConsts.PROF_ELEMENTALIST_AIR:
		case DWConsts.PROF_ELEMENTALIST_DARKNESS:
		case DWConsts.PROF_ELEMENTALIST_EARTH:
		case DWConsts.PROF_ELEMENTALIST_FIRE:
		case DWConsts.PROF_ELEMENTALIST_WATER:
			break;
		case DWConsts.PROF_KNIGHT:
			if (pc.getRank() >= 8) {
				if (!pc.hasSkill(DWConsts.SKILL_KNIGHT_DISARM)) {
					skills |= DWConsts.SKILL_KNIGHT_DISARM;
				}
				if (!pc.hasSkill(DWConsts.SKILL_KNIGHT_MAIN_GAUCHE)) {
					skills |= DWConsts.SKILL_KNIGHT_MAIN_GAUCHE;
				}
				if (!pc.hasSkill(DWConsts.SKILL_KNIGHT_MASTER_BOWMAN)) {
					skills |= DWConsts.SKILL_KNIGHT_MASTER_BOWMAN;
				}
				if (!pc.hasSkill(DWConsts.SKILL_KNIGHT_PARRY)) {
					skills |= DWConsts.SKILL_KNIGHT_PARRY;
				}
				if (!pc.hasSkill(DWConsts.SKILL_KNIGHT_QUICK_DRAW)) {
					skills |= DWConsts.SKILL_KNIGHT_QUICK_DRAW;
				}
				if (!pc.hasSkill(DWConsts.SKILL_KNIGHT_SWORDMASTER)) {
					skills |= DWConsts.SKILL_KNIGHT_SWORDMASTER;
				}
				if (!pc.hasSkill(DWConsts.SKILL_KNIGHT_WEAPONSKILL_AXE)) {
					skills |= DWConsts.SKILL_KNIGHT_WEAPONSKILL_AXE;
				}
				if (!pc.hasSkill(DWConsts.SKILL_KNIGHT_WEAPONSKILL_BLUNT)) {
					skills |= DWConsts.SKILL_KNIGHT_WEAPONSKILL_BLUNT;
				}
				if (!pc.hasSkill(DWConsts.SKILL_KNIGHT_WEAPONSKILL_EDGED)) {
					skills |= DWConsts.SKILL_KNIGHT_WEAPONSKILL_EDGED;
				}
				if (!pc.hasSkill(DWConsts.SKILL_KNIGHT_WEAPONSKILL_POLEARM)) {
					skills |= DWConsts.SKILL_KNIGHT_WEAPONSKILL_POLEARM;
				}				
			}
			break;
		case DWConsts.PROF_MYSTIC:
			break;
		case DWConsts.PROF_SORCERER:
			break;
		default: // WARLOCK
			break;
		}
		return skills;
	}
	public void rollBackground(final int profession) {
		int roll = (int) (Diceroller.getInstance().rollPercent() * 100f);
		switch (profession) {
		case DWConsts.PROF_KNIGHT:
			if (roll <= 18) {
				
			}
			break;
		}
	}
	/**
	 * Assigns a profession to the PC.
	 * @param pc the {@link DWCharacter}
	 * @param profession the assigned profession
	 */
	public void assignProfession(final DWCharacter pc, final int profession) {
		pc.setProfession(profession);
		pc.clearSkills();
		pc.setRank(1);
		// assign health points and combat factors
		switch (profession) {
		case DWConsts.PROF_ASSASSIN:
			pc.setHealthPoints(Diceroller.getInstance().rolldX(6) + 5);
			pc.setMaxHealthPoints(pc.getHealthPoints());
			pc.setAttack(13);
			pc.setDefence(5);
			pc.setMagicalDefence(3);
			pc.setEvasion(5);
			pc.setStealth(18);
			pc.setPerception(8);
			pc.assignSkill(DWConsts.SKILL_ASSASSIN_STEALTH);
			break;
		case DWConsts.PROF_BARBARIAN:
			pc.setHealthPoints(Diceroller.getInstance().rolldX(6) + 9);
			pc.setMaxHealthPoints(pc.getHealthPoints());
			pc.setAttack(14);
			pc.setDefence(6);
			pc.setMagicalDefence(3);
			pc.setEvasion(5);
			pc.setStealth(13);
			pc.setPerception(5);
			pc.assignSkill(DWConsts.SKILL_BARBARIAN_BERSERK);
			pc.assignSkill(DWConsts.SKILL_BARBARIAN_RIDE_WARHORSE);
			pc.assignSkill(DWConsts.SKILL_BARBARIAN_TRACK);
			break;
		case DWConsts.PROF_ELEMENTALIST_AIR:
		case DWConsts.PROF_ELEMENTALIST_DARKNESS:
		case DWConsts.PROF_ELEMENTALIST_EARTH:
		case DWConsts.PROF_ELEMENTALIST_FIRE:
		case DWConsts.PROF_ELEMENTALIST_WATER:
			pc.setHealthPoints(Diceroller.getInstance().rolldX(6) + 4);
			pc.setMaxHealthPoints(pc.getHealthPoints());
			pc.setMagicPoints(5);
			pc.setMaxMagicPoints(5);
			pc.setAttack(11);
			pc.setDefence(5);
			pc.setMagicalDefence(5);
			pc.setMagicalAttack(15);
			pc.setEvasion(3);
			pc.setStealth(13);
			pc.setPerception(5);
			pc.assignSkill(DWConsts.SKILL_ELEMENTALIST_SPELLCASTING);
			if (profession == DWConsts.PROF_ELEMENTALIST_AIR
					|| profession == DWConsts.PROF_ELEMENTALIST_EARTH) {
				pc.setSubsidiaryElementalistSpecializations(
						DWConsts.ELEM_FIRE, DWConsts.ELEM_WATER);
			}
			if (profession == DWConsts.PROF_ELEMENTALIST_FIRE
					|| profession == DWConsts.PROF_ELEMENTALIST_WATER) {
				pc.setSubsidiaryElementalistSpecializations(
						DWConsts.ELEM_AIR, DWConsts.ELEM_EARTH);
			}
			break;
		case DWConsts.PROF_KNIGHT:
			pc.setHealthPoints(Diceroller.getInstance().rolldX(6) + 7);
			pc.setMaxHealthPoints(pc.getHealthPoints());
			pc.setAttack(13);
			pc.setDefence(7);
			pc.setMagicalDefence(3);
			pc.setEvasion(4);
			pc.setStealth(13);
			pc.setPerception(5);
			pc.assignSkill(DWConsts.SKILL_KNIGHT_TRACK);
			pc.assignSkill(DWConsts.SKILL_KNIGHT_ARMOUR_EXPERT);
			pc.assignSkill(DWConsts.SKILL_KNIGHT_RIDE_WARHORSE);
			break;
		case DWConsts.PROF_MYSTIC:
			pc.setHealthPoints(Diceroller.getInstance().rolldX(6) + 5);
			pc.setMaxHealthPoints(pc.getHealthPoints());
			pc.setAttack(12);
			pc.setDefence(6);
			pc.setMagicalDefence(4);
			pc.setMagicalAttack(14);
			pc.setEvasion(3);
			pc.setStealth(14);
			pc.setPerception(6);
			pc.assignSkill(DWConsts.SKILL_MYSTIC_SPELLCASTING);
			pc.assignSkill(DWConsts.SKILL_MYSTIC_SPELL_MASTERY);
			pc.assignSkill(DWConsts.SKILL_MYSTIC_PREMONITION);
			pc.assignSkill(DWConsts.SKILL_MYSTIC_ESP);
			break;
		case DWConsts.PROF_SORCERER:
			pc.setHealthPoints(Diceroller.getInstance().rolldX(6) + 4);
			pc.setMaxHealthPoints(pc.getHealthPoints());
			pc.setMagicPoints(4);
			pc.setMaxMagicPoints(4);
			pc.setAttack(11);
			pc.setDefence(5);
			pc.setMagicalDefence(5);
			pc.setMagicalAttack(15);
			pc.setEvasion(3);
			pc.setStealth(13);
			pc.setPerception(5);
			pc.assignSkill(DWConsts.SKILL_SORCERER_SPELLCASTING);
			break;
		default: // WARLOCK
			pc.setHealthPoints(Diceroller.getInstance().rolldX(6) + 5);
			pc.setMaxHealthPoints(pc.getHealthPoints());
			pc.setMagicPoints(2);
			pc.setMaxMagicPoints(2);
			pc.setAttack(12);
			pc.setDefence(5);
			pc.setMagicalDefence(4);
			pc.setMagicalAttack(12);
			pc.setEvasion(3);
			pc.setStealth(13);
			pc.setPerception(5);
			pc.assignSkill(DWConsts.SKILL_WARLOCK_SPELLCASTING);
			break;
		}
		computeFullStats(pc);
	}
	/**
	 * Computes the full values for all base characteristics.
	 * @param pc the {@link DWCharacter}
	 */
	private void computeFullCharacteristics(final DWCharacter pc) {
		int strength = pc.getStrength();
		strength += pc.getModStrength();
		pc.setFullStrength(strength);

		int reflexes = pc.getReflexes();
		reflexes += pc.getModReflexes();
		pc.setFullReflexes(reflexes);

		int intelligence = pc.getIntelligence();
		intelligence += pc.getModIntelligence();
		pc.setFullIntelligence(intelligence);

		int psychicTalent = pc.getPsychicTalent();
		psychicTalent += pc.getModPsychicTalent();
		pc.setFullPsychicTalent(psychicTalent);

		int looks = pc.getLooks();
		looks += pc.getModLooks();
		pc.setFullLooks(looks);

		int healthPoints = pc.getHealthPoints();
		healthPoints += pc.getModHealthPoints();
		pc.setFullHealthPoints(healthPoints);

		int maxHealthPoints = pc.getMaxHealthPoints();
		maxHealthPoints += pc.getModMaxHealthPoints();
		pc.setFullMaxHealthPoints(maxHealthPoints);
		
		int magicPoints = pc.getMagicPoints();
		magicPoints += pc.getModMagicPoints();
		pc.setFullMagicPoints(magicPoints);

		int maxMagicPoints = pc.getMaxMagicPoints();
		maxMagicPoints += pc.getModMaxMagicPoints();
		pc.setFullMaxMagicPoints(maxMagicPoints);
	}
	private void computeSpellMods(final DWCharacter pc) {
		
	}
	public String getRankString(final DWCharacter pc) {
		PooledStringBuilder sb =
				StringBuilderPool.getInstance().getStringBuilder();
		int index = 0;
		if (pc.getRank() > 1) {
			index = pc.getRank() - 1;
		}
		if (index >= DWConsts.RANKS.length - 1) {
			index = DWConsts.RANKS.length - 1;
			sb.append(pc.getRank());
		}
		sb.append(DWConsts.RANKS[index]);
		String s = sb.toString();
		sb.returnToPool();
		return s;
	}
	private void computeEquipmentMods(final DWCharacter pc) {
		
	}
	/**
	 * Computes the full values for all base characteristics.
	 * @param pc the {@link DWCharacter}
	 */
	private void computeFullFactors(final DWCharacter pc) {
		// apply modifications based on the PC's characteristic scores
		// STRENGTH
		switch (pc.getFullStrength()) {
		case 3:
		case 4:
		case 5:
			pc.setModAttack(pc.getModAttack() - 2);
			pc.setModDefence(pc.getModDefence() - 1);
			break;
		case 6:
		case 7:
		case 8:
			pc.setModAttack(pc.getModAttack() - 1);
			break;
		case 13:
		case 14:
		case 15:
			pc.setModAttack(pc.getModAttack() + 1);
			break;
		case 16:
		case 17:
		case 18:
			pc.setModAttack(pc.getModAttack() + 2);
			pc.setModDefence(pc.getModDefence() + 1);
			break;
			default: 
				break;
		}
		// REFLEXES
		switch (pc.getFullReflexes()) {
		case 3:
		case 4:
		case 5:
			pc.setModAttack(pc.getModAttack() - 1);
			pc.setModDefence(pc.getModDefence() - 2);
			pc.setModEvasion(pc.getModEvasion() - 2);
			pc.setModStealth(pc.getModStealth() - 1);
			break;
		case 6:
		case 7:
		case 8:
			pc.setModDefence(pc.getModDefence() - 1);
			pc.setModEvasion(pc.getModEvasion() - 1);
			break;
		case 13:
		case 14:
		case 15:
			pc.setModDefence(pc.getModDefence() + 1);
			pc.setModEvasion(pc.getModEvasion() + 1);
			break;
		case 16:
		case 17:
		case 18:
			pc.setModAttack(pc.getModAttack() + 1);
			pc.setModDefence(pc.getModDefence() + 2);
			pc.setModEvasion(pc.getModEvasion() + 2);
			pc.setModStealth(pc.getModStealth() + 1);
			break;
			default: 
				break;
		}
		// INTELLIGENCE
		switch (pc.getFullIntelligence()) {
		case 3:
		case 4:
		case 5:
			pc.setModAttack(pc.getModAttack() - 1);
			pc.setModDefence(pc.getModDefence() - 1);
			pc.setModMagicalAttack(pc.getModMagicalAttack() - 1);
			pc.setModMagicalDefence(pc.getModMagicalDefence() - 1);
			break;
		case 16:
		case 17:
		case 18:
			pc.setModAttack(pc.getModAttack() + 1);
			pc.setModDefence(pc.getModDefence() + 1);
			pc.setModMagicalAttack(pc.getModMagicalAttack() + 1);
			pc.setModMagicalDefence(pc.getModMagicalDefence() + 1);
			break;
			default: 
				break;
		}
		// PSYCHIC TALENT
		switch (pc.getFullReflexes()) {
		case 3:
		case 4:
		case 5:
			pc.setModMagicalDefence(pc.getModMagicalDefence() - 2);
			pc.setModPerception(pc.getModPerception() - 1);
			break;
		case 6:
		case 7:
		case 8:
			pc.setModMagicalDefence(pc.getModMagicalDefence() - 1);
			break;
		case 13:
		case 14:
		case 15:
			pc.setModMagicalDefence(pc.getModMagicalDefence() + 1);
			pc.setModMagicalAttack(pc.getModMagicalAttack() + 1);
			break;
		case 16:
		case 17:
		case 18:
			pc.setModMagicalDefence(pc.getModMagicalDefence() + 2);
			pc.setModPerception(pc.getModPerception() + 1);
			pc.setModMagicalAttack(pc.getModMagicalAttack() + 2);
			break;
			default: 
				break;
		}
		int attack = pc.getAttack();
		attack += pc.getModAttack();
		pc.setFullAttack(attack);

		int defence = pc.getDefence();
		defence += pc.getModDefence();
		pc.setFullDefence(defence);
		
		int magicalAttack = pc.getMagicalAttack();
		magicalAttack += pc.getModMagicalAttack();
		pc.setFullMagicalAttack(magicalAttack);

		int magicalDefence = pc.getMagicalDefence();
		magicalDefence += pc.getModMagicalDefence();
		pc.setFullMagicalDefence(magicalDefence);
		
		int evasion = pc.getEvasion();
		evasion += pc.getModEvasion();
		pc.setFullEvasion(evasion);

		int stealth = pc.getStealth();
		stealth += pc.getModStealth();
		pc.setFullStealth(stealth);

		int perception = pc.getPerception();
		perception += pc.getModPerception();
		pc.setFullPerception(perception);
	}
	/**
	 * Computes the full statistics for a PC.
	 * @param pc the {@link DWCharacter}
	 */
	public void computeFullStats(final DWCharacter pc) {
		this.computeEquipmentMods(pc);
		this.computeSpellMods(pc);
		computeFullCharacteristics(pc);
		computeFullFactors(pc);
		pc.notifyWatchers();
	}
	/**
	 * Gets the best 3 rolls out of 4, a method used when generating a new
	 * {@link DWCharacter}.
	 * @param args the list of rolls
	 * @return int
	 */
	private int getBest3Of4(final int... args) {
		int sum = 0, min = 6;
		// get the lowest dice roll
		for (int i = 0; i < args.length; i++) {
			min = Math.min(min, args[i]);
		}
		// sum up all rolls except the lowest
		boolean skippedLowest = false;
		for (int i = 0; i < args.length; i++) {
			if (!skippedLowest
					&& args[i] == min) {
				skippedLowest = true;
				continue;
			}
			sum += args[i];
		}
		return sum;
	}
	/**
	 * Rolls a characterstic score, returning a value between 3 and 18.
	 * @return int
	 */
	public int rollCharacteristicScore() {
		return getBest3Of4(new int[] {
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6)
				});
	}
	/**
	 * Creates a "fresh" hero with no profession chosen.
	 * @return {@link DWCharacter}
	 */
	public DWCharacter getFreshHero() {
		DWCharacter pc = new DWCharacter();
		getFreshHero(pc);
		return pc;
	}
	public void getFreshHero(final DWCharacter pc) {
		// roll random characteristics
		pc.setStrength(getBest3Of4(new int[] {
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6)
				}));
		pc.setReflexes(getBest3Of4(new int[] {
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6)
				}));
		pc.setIntelligence(getBest3Of4(new int[] {
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6)
				}));
		pc.setPsychicTalent(getBest3Of4(new int[] {
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6)
				}));
		pc.setLooks(getBest3Of4(new int[] {
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6),
				Diceroller.getInstance().rolldX(6)
				}));
		// re-roll lowest score
		int minScore = 18, flags = 0;
		minScore = Math.min(minScore, pc.getStrength());
		minScore = Math.min(minScore, pc.getReflexes());
		minScore = Math.min(minScore, pc.getIntelligence());
		minScore = Math.min(minScore, pc.getPsychicTalent());
		minScore = Math.min(minScore, pc.getLooks());
		if (minScore != 18) {
			// tally up flags
			if (minScore == pc.getStrength()) {
				flags = 1;
			}
			if (minScore == pc.getReflexes()) {
				if (flags != 0) { // a flag already set
					if (Diceroller.getInstance().rolldX(6) > 3) {
						// remove old flag on a roll of 4, 5, 6
						flags = 2;
					}
				} else {
					flags = 2;
				}
			}
			if (minScore == pc.getIntelligence()) {
				if (flags != 0) { // a flag already set
					if (Diceroller.getInstance().rolldX(6) > 3) {
						// remove old flag on a roll of 4, 5, 6
						flags = 3;
					}
				} else {
					flags = 3;
				}
			}
			if (minScore == pc.getPsychicTalent()) {
				if (flags != 0) { // a flag already set
					if (Diceroller.getInstance().rolldX(6) > 3) {
						// remove old flag on a roll of 4, 5, 6
						flags = 4;
					}
				} else {
					flags = 4;
				}
			}
			if (minScore == pc.getLooks()) {
				if (flags != 0) { // a flag already set
					if (Diceroller.getInstance().rolldX(6) > 3) {
						// remove old flag on a roll of 4, 5, 6
						flags = 5;
					}
				} else {
					flags = 5;
				}
			}
			switch (flags) {
			case 1:
				pc.setStrength(Math.max(pc.getStrength(),
						getBest3Of4(new int[] {
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6)
				})));
				break;
			case 2:
				pc.setReflexes(Math.max(pc.getReflexes(),
						getBest3Of4(new int[] {
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6)
				})));
				break;
			case 3:
				pc.setIntelligence(Math.max(pc.getIntelligence(),
						getBest3Of4(new int[] {
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6)
				})));
				break;
			case 4:
				pc.setPsychicTalent(Math.max(pc.getPsychicTalent(),
						getBest3Of4(new int[] {
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6)
				})));
				break;
			case 5:
				pc.setLooks(Math.max(pc.getLooks(),
						getBest3Of4(new int[] {
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6),
								Diceroller.getInstance().rolldX(6)
				})));
				break;
			}
		}
		computeFullStats(pc);
	}
}
